package tg1;

 import java.io.*;
 import java.net.*;
 import java.sql.*;
 
 import tg1.JdbcDAO;
 
 public class TrackerRefresher {
 
	private static long lastUpdate;
	
	private static byte[] getPeerChanges(){
	
		Connection conn=null;
		try{
			conn= JdbcDAO.getDBConnection();
		int ANNOUNCE_SIMPLES, STARTED, STOPPED, COMPLETED;
		
		if (TrackerProperties.getProperty("tracker.ratio.RATIO_SYSTEM_ACTIVE").equals ("false")){
			ANNOUNCE_SIMPLES =0;
			STARTED=1;
			STOPPED= 2;
			COMPLETED=3;
		}
		else{
			ANNOUNCE_SIMPLES =4;
			STARTED=5;
			STOPPED= 6;
			COMPLETED=7;
		}
		
		long interval =System.currentTimeMillis() - (Long.parseLong(TrackerProperties.getTrackerProperties().getProperty("tracker.refresh.INTERVAL")) *1000);
		
		
		ResultSet hashRs=JdbcDAO.getAmountOfModifiedPeers(conn,interval);
		ResultSet peerRs = JdbcDAO.getModifiedPeers(conn,interval);
		
		int numTorrents=0, numPeers=0;
		
		if (hashRs.next()){
			
			hashRs.last();	
			numTorrents= hashRs.getRow();
			hashRs.beforeFirst();
			System.out.println ("getPeerChanges: ResultSet  tem "+numTorrents+"hashes");
		}
		else{ System.out.println ("getPeerChanges: ResultSet retorna nulo: Nenhum peer pra dar refresh"); 
			return null;}
		
		if (peerRs.next()){
			peerRs.last();	
			numPeers= peerRs.getRow();
			peerRs.beforeFirst();
			System.out.println ("getPeerChanges: ResultSet  tem "+numPeers+"peers");
		}
		else{ System.out.println ("getPeerChanges: ResultSet retorna nulo: Nenhum peer pra dar refresh"); 
			return null;}
		
		//o 7 eh: 4 bytes do IP, 2 bytes da porta, e 1 byte de versao do protocolo de refresh do tracker
		//byte[] modifiedPeers = new byte[ (numTorrents*20) + (numPeers*51) + 7];
		byte[] modifiedPeers;	
		if (TrackerProperties.getProperty("tracker.ratio.RATIO_SYSTEM_ACTIVE").equals ("true")){
			modifiedPeers = new byte[ (numTorrents*22) + (numPeers*51) + 7];
		}else { modifiedPeers = new byte[ (numTorrents*22) + (numPeers*35) + 7];}	
		
		System.out.println ("getPeerChanges: bytes alocados p/ modifiedPeers: "+modifiedPeers.length);
		
		int numrows=0, numHashes=0, index=0, acao=0,  localPort=0, peerPort=0, protocolVersion=0, i=0, k=0;
		long upload=0, downloaded=0, timestamp=0;
		byte[] byteInfo, localIP, bytePeer_id, peerIp, hashEmBytes ;
		
		localIP = InetAddress.getLocalHost().getAddress();
		for (i =0; i<4; i++)
			{ modifiedPeers[index++] = localIP[i]; }
		//index+=4;
		
		//assumindo POR ENQUANTO que todos os trackers funfam na porta 2000
		localPort= 2000;					
		modifiedPeers[index]= (byte)(localPort>>>8);
		modifiedPeers[index+1]= (byte)(localPort);
		index+=2;
		
		protocolVersion=1;
		modifiedPeers[index++]=(byte) protocolVersion;
		
		peerRs.next();
		System.out.println ("modifiedPeers INICIO"+new String(modifiedPeers, "UTF-8"));
		while (hashRs.next()){
			
			numHashes= hashRs.getInt("count");
			System.out.println ("numHashes="+numHashes);
			modifiedPeers[index++]= (byte) (numHashes >>>8);
			modifiedPeers[index++]= (byte) (numHashes );
			//index+=2;
			
			hashEmBytes=peerRs.getString("info_hash").getBytes("UTF-8");
			System.out.println ("HASH ATUAL="+new String (hashEmBytes, "UTF-8"));
			for (i =0; i<20; i++)
				{ modifiedPeers[index++] = hashEmBytes[i]; }			
			//index+=20;
			i=0;
			for (k=0; k<numHashes; k++){
				System.out.println ("hash atual="+k);
				System.out.println ("index="+index);
				bytePeer_id = peerRs.getString("peer_id").getBytes("UTF-8");
				System.out.println ("bytePeer_id="+new String (bytePeer_id, "UTF-8"));
				for (i =0; i<20; i++)
					{ modifiedPeers[index++] = bytePeer_id[i]; }			
				//index+=20;
				
				if (peerRs.getBoolean("started") == true )
					{ acao=1;	}
				else if (peerRs.getBoolean("stopped") == true )
					{ acao=2;	}
				else if (peerRs.getBoolean("completed") == true )
					{ acao=3;	}
				
				modifiedPeers[index++]= (byte)acao;
				//index++;
				System.out.println ("acao="+acao);
				timestamp= peerRs.getLong("lastupdate");
				System.out.println ("timestamp="+Long.toBinaryString(timestamp));
				for (i =7; i>=0; i--){
					int aux = (int)(timestamp>>>(8*i) );
					System.out.println ("octeto atual do timestamp"+ Integer.toBinaryString(aux));
					modifiedPeers[index+i] =(byte) aux; 
				}			
				index+=8;
				
				peerIp = peerRs.getBytes("ipembytes");
				System.out.println ("ipembytes="+new String(peerIp, "UTF-8"));
				
				for (i =0; i<4; i++)
					{ modifiedPeers[index++] = peerIp[i]; }		
				
				peerPort=peerRs.getInt("port");
				System.out.println ("peerPort="+peerPort);
				modifiedPeers[index++]= (byte) (peerPort >>>8);
				modifiedPeers[index++]= (byte) (peerPort);
				
				if (TrackerProperties.getProperty("tracker.ratio.RATIO_SYSTEM_ACTIVE").equals ("true")){
					upload= peerRs.getLong("uploaded");
					/*for (i =0; i<8; i++)
						{ modifiedPeers[index+i] = byteUpload[i]; }			
					*/
					for (i =7; i<=0; i--)
						{ modifiedPeers[index+i] =(byte) (upload>>>(8*i) ) ; }			
					index+=8;
					
					downloaded= peerRs.getLong("downloaded");
					for (i =7; i<=0; i--)
						{ modifiedPeers[index+i] =(byte) (downloaded>>>(8*i) ) ; }			
					index+=8;
				}
				
				peerRs.next();
			}
		}
		
		hashRs.close();
		peerRs.close();
		System.out.println ("INDEX:  "+index);
		System.out.println ("FIM DA CODIFICACAO ");
		// linhas de debug xD
/*		String modifiedPeersSTR =new String(modifiedPeers, "ASCII");
		System.out.println ("modifiedPeers: ASCII "+modifiedPeersSTR);
		System.out.println ("modifiedPeers : length "+modifiedPeersSTR.length());
		modifiedPeersSTR =new String(modifiedPeers, "UTF-8");
		System.out.println ("modifiedPeers: UTF-8 "+modifiedPeersSTR);
		System.out.println ("modifiedPeers : length "+modifiedPeersSTR.length());
*/		
		return modifiedPeers;	 
		} catch (Exception e){
			System.out.println ("Erro em getPeerChanges:");
			e.printStackTrace();
			return null;
		}
		finally{/*retorna a conexao para o pool*/ JdbcDAO.closeConnection(conn);	}
	}
	
	/*********************************************************************************************************/
	/*********************************************************************************************************/
	private static void sendModifiedPeers(){
		
		try{
			int i=0;
			Socket s;
			OutputStream os;
			InetAddress[] trackers = InetAddress.getAllByName("shogouki.tg.");
			
			byte[] modifiedPeers = getPeerChanges();
			
			/*se nao tiver nenhum peer pra "refreshar"*/
			if (modifiedPeers == null)
				{return;}
			
			for (i=0; i<trackers.length; i++){
				s= new Socket(trackers[i], 2000);
				os = s.getOutputStream();
				os.write(modifiedPeers);
				os.close();
				s.close();
			}	
		}catch (Exception ex) {
			System.out.println(ex.getMessage());
			ex.printStackTrace();
		}

	}
	
	/*********************************************************************************************************/
	/*********************************************************************************************************/
	
	public static void refreshTrackerIfNeeded(){
		
		//milliseconds para proximo refresh
		//usando 5 minutos
		int interval = Integer.parseInt(TrackerProperties.getTrackerProperties().getProperty("tracker.refresh.INTERVAL"))*1000;
		long timeInMiliis = System.currentTimeMillis();
		
		//soh leva em conta numero de peers adicionados e stopped pra calcular se precisa fazer refresh no tracker. nesse caso, ratio eh  secundario, por isso nao eh considerado
		int numModificacoes= JdbcDAO.getTotalNewPeers() + JdbcDAO.getTotalStoppedPeers();
		
		int maxModificacoes= Integer.parseInt(TrackerProperties.getTrackerProperties().getProperty("tracker.refresh.MAX_ALTERATIONS_UNTIL_REFRESH"));
		
		if ( (timeInMiliis > (lastUpdate + interval)) || numModificacoes > maxModificacoes){
			//sendModifiedPeers();
			byte[] peerChanges = getPeerChanges();
			System.out.println(peerChanges);
			//updateTracker(peerChanges);
			lastUpdate = timeInMiliis;
		}
	}
	
	/****************************************************************************/
	/****************************************************************************/		
	
	public static void updateTracker(byte[] modifiedPeers){

		try{
			 int trackerIp=0, trackerPort=0, versaoProtocolo=0, numHashes=0, acao=0, index=0, peerIP=0, peerPort=0,  paramIndex=0, i=0, k=0, aux=0;
			long timestamp=0, uploaded=0, downloaded=0;
			byte[] peer_idEmBytes= new byte[20], byteIP= new byte[4];
			String peer_id, info_hash;
			StringBuffer updateQuery= new StringBuffer();
					
			/*desecondando ip*/
			System.out.print("trackerIp : " );
			for (i=0;i<4;i++){
				trackerIp = trackerIp << 8;
				/*explicacao do codigo abaixo:
				java trata TODOS os inteiros em complemento de 2. entao ao dar cast de um byte pra int, 
				o valor que o byte teria se fosse inteiro (em compl de 2) eh atribuido ao int. 
				Entao se o bit mais significativo for 1, todos os bits antes dele vao ser setados em 1 pra 
				preservar o sinal negativo do int retirado do byte.
				como todos os inteiros no protocolo sao sem sinal, tem que pegar o valor do byte depois do cast 
				e tirar os bits 1 da possivel conversao. um and do valor depois do cast com 255 (0x000f) resolve.*/
				System.out.println("index"+index);	
				trackerIp =trackerIp | ((int) modifiedPeers[index++]  & 255);
				
			}
			System.out.println(Integer.toBinaryString(trackerIp));
			System.out.println(trackerIp);
			for (i=0;i<2;i++){
				System.out.println("index"+index);	
				System.out.println("\nocteto atual da porta:"+ Integer.toBinaryString(modifiedPeers[index]));
				trackerPort = trackerPort << 8;
				trackerPort =  trackerPort | ((int) modifiedPeers[index++]  & 255);
				System.out.println("trackerPort atual    :"+ Integer.toBinaryString(trackerPort));
				System.out.println("trackerPort atual    :"+trackerPort);
				
			}
			System.out.println("trackerPort           " + Integer.toBinaryString(trackerPort));
			System.out.println("index"+index);	
			versaoProtocolo = (((int) modifiedPeers[index++] ) & 255);
			System.out.println("versaoProtocolo=" +Integer.toBinaryString(versaoProtocolo));
			System.out.println("index"+index);	
			System.out.println("comecando a tratar hashes de torrents");	
			
			/**por as linhas abaixo num loop que percorra todos os info_hashes*/
			while (index< modifiedPeers.length){		
				System.out.println("index"+index);	
				numHashes=0;
				for (i=0;i<2;i++){
					System.out.println("index"+index);	
					System.out.println("octeto atual do numHashes:"+ Integer.toBinaryString( ((int) modifiedPeers[index])  & 255));
					numHashes = numHashes << 8;
					numHashes =numHashes| (((int) modifiedPeers[index++])  & 255);
				}
				System.out.println("index:" + index);
				System.out.println("numHashes:"+ Integer.toBinaryString( numHashes));
				System.out.println("numHashes" + numHashes);
				
				info_hash= new String (modifiedPeers, index, 20, "UTF-8");
				System.out.println("info_hash" + info_hash);
				index+=20;		
				System.out.println("index:" + index);
				System.out.println("comecando a iterar sobre peer_ids com esse infohash");
				for (k=0; k< numHashes; k++){
					System.out.println("*******hashAtual="+k);	
					peer_id = new String (modifiedPeers, index, 20, "UTF-8");
					index+=20;
					System.out.println("peer_id"+peer_id);	
					
					System.out.println("index:" + index);
					
					acao = ((int) modifiedPeers[index++] ) & 255;
					System.out.println("acao="+acao);	
					for (i=0;i<8;i++){
						System.out.println("index"+index);	
						timestamp=timestamp << 8;
						timestamp =timestamp |( ((int) modifiedPeers[index++] ) & 255);
					}
					System.out.println("timestamp"+Long.toBinaryString(timestamp));	
					
					for (i=0;i<4;i++){
						System.out.println("index"+index);	
						peerIP= peerIP << 8;
						peerIP=peerIP | (((int) modifiedPeers[index] ) & 255);
						byteIP[i]= modifiedPeers[index];
						index++;
					}
					System.out.println("peerIP"+Integer.toBinaryString(peerIP));	
					System.out.println("index"+index);	
					
					for (i=0;i<2;i++){
						System.out.println("index"+index);	
						peerPort= peerPort << 8;
						peerPort=peerPort | ( ((int) modifiedPeers[index++]  )& 255);
					}
					System.out.println("peerPort"+Integer.toBinaryString(peerPort));	
					System.out.print("peerPort");	
					System.out.println(peerPort);
					
					if (TrackerProperties.getProperty("tracker.ratio.RATIO_SYSTEM_ACTIVE").equals ("true")){
						for (i=0;i<8;i++){
							System.out.println("index"+index);	
							uploaded= uploaded << 8;
							uploaded=uploaded | (((int) modifiedPeers[index++] ) & 255);
						}
					        System.out.println("uploaded"+Long.toBinaryString(uploaded));	
						System.out.print("uploaded");	
						System.out.print(uploaded);
						for (i=0;i<8;i++){
							System.out.println("index"+index);	
							downloaded= downloaded << 8;
							downloaded=downloaded | (((int) modifiedPeers[index++]  )& 255);
						}
						System.out.println("downloaded"+Long.toBinaryString(downloaded));	
						System.out.print("downloaded");	
						System.out.print(downloaded);
					}
					
					//announce simples
					//System.out.print("acao "+acao);	
					if ( (acao == 1) || ( acao == 4)){
						
						JdbcDAO.addToInsertBatch(info_hash, peer_id, peerIP, peerPort, byteIP, timestamp);
						/*updateQuery.append ( JdbcDAO.insertNewPeerQuery);
						updateQuery.append(" ");
						
						paramIndex=updateQuery.indexOf("?");
						System.out.print("info_hash: index do ?= "+paramIndex);	
						updateQuery.deleteCharAt(paramIndex);
						updateQuery.insert(paramIndex, info_hash);
								
						paramIndex=updateQuery.indexOf("?");
						System.out.print("peer_id:index do ?= "+paramIndex);	
						updateQuery.deleteCharAt(paramIndex);
						updateQuery.insert(paramIndex, peer_id);
						
						paramIndex=updateQuery.indexOf("?");
						System.out.print("peerIP:index do ?= "+paramIndex);	
						updateQuery.deleteCharAt(paramIndex);
						updateQuery.insert(paramIndex, peerIP);
						
						paramIndex=updateQuery.indexOf("?");
						System.out.print("peerPort:index do ?= "+paramIndex);	
						updateQuery.deleteCharAt(paramIndex);
						updateQuery.insert(paramIndex, peerPort);
						
						//TALVEZ ACONTECA NAS 3 LINHAS ABAIXO O MESMO PROBLEMA DO BENCODE:
						//NAO CONSEGUIR POR VALORES NAO ASCII NA STRING. 
						paramIndex=updateQuery.indexOf("?");
						System.out.print("byteIP:index do ?= "+paramIndex);	
						updateQuery.deleteCharAt(paramIndex);
						updateQuery.insert(paramIndex, byteIP);
						
						paramIndex=updateQuery.indexOf("?");
						System.out.print("index do ?= "+paramIndex);	
						updateQuery.deleteCharAt(paramIndex);
						updateQuery.insert(paramIndex, timestamp);
						*/
						//TODO soh pode inserir na history se o torrent nao estiver la ainda
						if (TrackerProperties.getProperty("tracker.ratio.RATIO_SYSTEM_ACTIVE").equals ("true")){
							
							JdbcDAO.addToHistoryBatch(info_hash, peer_id);
							/*updateQuery.append(JdbcDAO.addTorrentToHistoryQuery);
							updateQuery.append(" ");
							
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, peer_id);
							
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, info_hash);
							*/
						}
					}
					else {
						//update comum tanto a um announce simples, quanto num stopped ou num completed
						if (acao > 4){
					
							JdbcDAO.addToRegisteredPeersBatch(info_hash, peer_id, downloaded, uploaded, timestamp );
							/*updateQuery.append(JdbcDAO.refreshRegisteredPeerQuery);
							updateQuery.append(" ");
							
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, timestamp);
		
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, peer_id);
		
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, info_hash);
							*/
						}
						else {
							
							JdbcDAO.addToUnregisteredPeersBatch(info_hash, peer_id, downloaded, uploaded, timestamp );
						
							/*updateQuery.append(JdbcDAO.refreshUnregisteredPeerQuery);}
						
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, downloaded);
							
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, uploaded);
							
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, timestamp);
		
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, peer_id);
		
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, info_hash);
							*/
						}
						//exclusivo do stopped
						if (acao == 2 || acao == 6 ){
							
							JdbcDAO.addToStoppedBatch(info_hash, peer_id);
							/*updateQuery.append(JdbcDAO.markPeerAsStoppedQuery);
							updateQuery.append(" ");
							
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, info_hash);
									
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, peer_id);
							*/
						}
						
						//exclusivo do completed
						if (acao == 3 || acao == 7 ){
							JdbcDAO.addToCompletedBatch(info_hash);
							/*updateQuery.append(JdbcDAO.increaseCompletedDownloadsQuery);
							updateQuery.append(" ");
		
							paramIndex=updateQuery.indexOf("?");
							updateQuery.deleteCharAt(paramIndex);
							updateQuery.insert(paramIndex, info_hash);
							*/
						}
					}	
					System.out.println("\n\n\n\n\njaspion!!!!!!");		
				}//fim for
				
			} //fim while
			
			//System.out.println("QUERY: "+ updateQuery.toString());		
			System.out.println("Executando query no banco");
			//JdbcDAO.refreshTracker(updateQuery.toString());
			JdbcDAO.executeBatches();
			System.out.println("Query executada no banco com sucesso");
			
		}catch (Exception ex) {
			System.out.println(ex.getMessage());
			ex.printStackTrace();
		}
	}
 }
		
	